package eu.fritschi.parachatcapture;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class ParachatMessageFilter {
	/**
	 * @param data - payload from the parachat network packet.
	 * @return null if the message cannot be converted into a ParachatMessage or a ParachatMessage if it can be converted.
	 */
	public static List<ParachatMessage> filterPacket(byte[] data) {
		List<ParachatMessage> messages = new ArrayList<ParachatMessage>();
		/* 
		 * From the network dump it seems as if chat messages have "c8 00 ca" at the start in the data. 
		 * This is followed by other data like the username, the message and optionally a format if the message is in 
		 * a certain color or style. It seems as if c8 00 ca 02 is for messages withouth style and c8 00 ca 03 is for 
		 * messages with style. 
		 * 
		 * Examples:
		 * 
		 * Just one message in the packet.
		 * 00000380  50 43 00 00 2b 00 07 00  c8 00 ca 02 23 4e 24 00 PC..+... ....#N$.
		 * 00000390  06 62 69 61 67 67 6f 23  4e 22 00 14 52 49 54 54 .biaggo# N"..RITT
		 * 000003A0  20 6e 65 61 72 20 35 2e  34 20 72 65 74 65 73 74  near 5. 4 retest
		 * 
		 * One message with a style.
		 * 000003E3  50 43 00 00 32 00 07 00  c8 00 ca 03 23 4e 24 00 PC..2... ....#N$.
		 * 000003F3  09 54 72 61 64 65 48 61  77 6b 23 4e 22 00 0a 47 .TradeHa wk#N"..G
		 * 00000403  4c 55 55 20 70 65 72 6b  79 23 4e 26 00 09 66 73 LUU perk y#N&..fs
		 * 00000413  74 79 6c 65 3a 42 0a 50  43 00 00 14 00 07 00 c8 tyle:B.P C.......
		 * 
		 * A message and before that some other information.
		 * 000004AA  50 43 00 00 75 00 07 00  c8 00 ca 03 23 4e 24 00 PC..u... ....#N$.
		 * 000004BA  08 79 61 64 61 79 61 64  61 23 4e 22 00 3f 52 4f .yadayad a#N".?RO
		 * 000004CA  53 47 20 73 68 6f 75 6c  64 20 68 61 76 65 20 73 SG shoul d have s
		 * 000004DA  74 72 6f 6e 67 20 73 75  70 70 6f 72 74 20 39 2e trong su pport 9.
		 * 000004EA  33 27 73 20 69 66 20 69  74 20 62 72 65 61 6b 73 3's if i t breaks
		 * 000004FA  20 64 6f 77 6e 20 66 75  72 74 68 65 72 23 4e 26  down fu rther#N&
		 * 0000050A  00 18 63 6f 6c 6f 72 3a  2d 36 37 35 30 32 30 38 ..color: -6750208
		 * 0000051A  0a 66 73 74 79 6c 65 3a  42 0a                   .fstyle: B.
		 * 
		 * Multiple messages in one packet.
		 * 0000   49 53 52 28 31 29 50 43 00 00 34 00 07 00 c8 00  ISR(1)PC..4.....
		 * 0010   ca 02 23 4e 24 00 04 4e 61 74 65 23 4e 22 00 1f  ..#N$..Nate#N"..
		 * 0020   59 61 20 77 68 61 74 20 61 72 65 20 79 6f 75 20  Ya what are you 
		 * 0030   67 75 79 73 20 73 6d 6f 6b 69 6e 67 20 3a 29 50  guys smoking :)P
		 * 0040   43 00 00 84 00 07 00 c8 00 ca 02 23 4e 24 00 04  C..........#N$..
		 * 0050   4e 61 74 65 23 4e 22 00 6f 4c 6f 6f 6b 73 20 6c  Nate#N".oLooks l
		 * 0060   69 6b 65 20 74 61 62 6c 65 73 20 77 65 72 65 20  ike tables were 
		 * 0070   74 75 72 6e 65 64 20 61 6e 64 20 64 61 6e 65 64  turned and daned
		 * 0080   6f 6d 65 20 61 6e 64 20 6d 74 63 75 74 62 6f 77  ome and mtcutbow
		 * 0090   20 77 65 72 65 20 74 68 65 20 6f 6e 65 73 20 77   were the ones w
		 * 00a0   69 74 68 20 74 68 65 20 67 6f 6f 64 20 77 65 65  ith the good wee
		 * 00b0   6b 65 6e 64 20 28 69 6e 74 6f 20 53 75 6e 64 61  kend (into Sunda
		 * 00c0   79 20 6e 69 67 68 74 29 50 43 00 00 68 00 07 00  y night)PC..h...
		 * 00d0   c8 00 ca 02 23 4e 24 00 04 4e 61 74 65 23 4e 22  ....#N$..Nate#N"
		 * 00e0   00 53 68 74 74 70 3a 2f 2f 77 77 77 2e 69 6e 76  .Shttp://www.inv
		 * 00f0   65 73 74 6f 72 73 75 6e 64 65 72 67 72 6f 75 6e  estorsundergroun
		 * 0100   64 2e 63 6f 6d 2f 32 30 31 32 2f 30 37 2f 31 35  d.com/2012/07/15
		 * 0110   2f 73 63 61 6e 73 2d 66 72 6f 6d 2d 69 6e 76 65  /scans-from-inve
		 * 0120   73 74 6f 72 73 6c 69 76 65 2d 66 6f 72 2d 30 37  storslive-for-07
		 * 0130   31 36 31 32 2f                                   1612/
		
		// A chat message preceded and trailed by another information message (no chat messages).
		0000   50 43 00 00 11 00 07 00 c8 00 d2 01 23 4e 20 00  PC..........#N .
		0010   05 77 63 65 73 71 50 43 00 00 29 00 07 00 c8 00  .wcesqPC..).....
		0020   ca 02 23 4e 24 00 06 64 5f 6d 69 6e 61 23 4e 22  ..#N$..d_mina#N"
		0030   00 12 67 6f 6f 64 20 6c 75 63 6b 20 65 76 65 72  ..good luck ever
		0040   79 6f 6e 65 50 43 00 00 0f 00 07 00 c8 00 e4 01  yonePC..........
		0050   23 4e 20 00 03 32 38 33 50 43 00 00 14 00 07 00  #N ..283PC......
		0060   c8 00 e0 01 23 4e 20 00 08 73 77 69 6d 73 77 69  ....#N ..swimswi
		0070   6d 50 43 00 00 24 00 07 00 c8 00 e6 02 23 4e 24  mPC..$.......#N$
		0080   00 08 73 77 69 6d 73 77 69 6d 23 4e 2a 00 0b 64  ..swimswim#N*..d
		0090   65 66 61 75 6c 74 2e 70 6e 67                    efault.png
		
		*/
		
		// Split up into PC message block (starts with 50 43 00 00)
		byte[] pcMessageIndicator = { 0x50, 0x43, 0x00, 0x00 };
		int pcMessageStartIndex = find(data, pcMessageIndicator);
		
		while (pcMessageStartIndex != -1) {
			int fromIndex = pcMessageStartIndex;
			// look for next message which is also the end of the current message.
			pcMessageStartIndex = find(data, pcMessageIndicator, pcMessageStartIndex+4);
			int toIndex = data.length;
			if (pcMessageStartIndex != -1) {
				toIndex = pcMessageStartIndex;
			}
			byte[] pcMessage = Arrays.copyOfRange(data, fromIndex, toIndex);
			ParachatMessage parachatMessage = extractParachatMessage(pcMessage);
			if (parachatMessage != null) {
				messages.add(parachatMessage);
			}
		}
		return messages;
	}

	private static ParachatMessage extractParachatMessage(byte[] data) {
		ParachatMessage parachatMessage = null;
		byte[] chatMessageStartIndicator = { (byte)0xc8, 0x00, (byte)0xca };
		int authorStartIndex = find(data, chatMessageStartIndicator);
		if (authorStartIndex != -1) {
			authorStartIndex += 9; // Forward to the authors name.
			byte[] messageStartIndicator = {0x23, 0x4e, 0x22, 0x00};
			byte[] styleStartIndicator = {0x23, 0x4e, 0x26, 0x00};
			int messageStartIndex = find(data, messageStartIndicator);
			// sanity check
			if (messageStartIndex == -1 || messageStartIndex <= authorStartIndex) {
				// pattern not found or the index is before index 17 which is the start of the username.
				return null;
			}
			int authorEndIndex = messageStartIndex;
			messageStartIndex += 5; // length of the indicator plus another byte which seems to change. Don't know what that is used for but that's how it is.
			int styleStartIndex = find(data, styleStartIndicator);
			int messageEndIndex = styleStartIndex == -1 ? data.length : styleStartIndex;
			String author = new String(data, authorStartIndex, authorEndIndex - authorStartIndex);
			String message = new String(data, messageStartIndex, messageEndIndex - messageStartIndex);
			parachatMessage = new ParachatMessage(System.currentTimeMillis(), author, message);			
		}
		return parachatMessage;
	}
	
	private static int find(byte[] source, byte[] match) {
		return find(source, match, 0);
	}
	
	private static int find(byte[] source, byte[] match, int  spos) { 
		/*
		 * Quick copy paste from http://www.coderanch.com/t/369639/java/java/finding-byte-subset-another-byte
		 */
	    // sanity checks  
	    if(source == null || match == null || spos >= source.length)  
	      return -1;  
	    if(source.length == 0 || match.length == 0)  
	      return -1;  
	    int ret = -1;  
	    int mpos = 0;  
	    byte m = match[mpos];  
	    for( ; spos < source.length; spos++ ) {  
	      if(m == source[spos]) {  
	        // starting match  
	        if(mpos == 0)  
	          ret = spos;  
	        // finishing match  
	        else if(mpos == match.length - 1)  
	          return ret;  
	        mpos++;  
	        m = match[mpos];  
	      }  
	      else {  
	        ret = -1;  
	        mpos = 0;  
	        m = match[mpos];  
	      }  
	    }  
	    return ret;  
	  }  
}
